package com.prashantdctm.utils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;


public class XMLUtil {
	
	static int numberLines = 0;
	static String[] displayText = new String[1000];
	
	public static void main (String[] args) throws Exception {
		
	}
	/**
	 * Converts an html document to xml
	 * @throws Exception
	 */
	public static void convertHTMLTOXML(String htmlFilePath, String xmlFilePath){
		try {
			Document htmlDoc = getDOMObject(htmlFilePath);
			Document xmlDoc = getNewDOMObject();
			Element xmlElement = null;
			Element htmlElement = null;
			
			//Create test node 
			xmlElement = xmlDoc.createElement("task");
			xmlDoc.appendChild(xmlElement);
			
			//get task and add to test document
			htmlElement = htmlDoc.getDocumentElement();
			//dumpNode(htmlElement, "");
			NodeList nodeList = htmlDoc.getElementsByTagName("div");
			for (int i = 0; i < nodeList.getLength(); i++) {
				xmlElement = xmlDoc.createElement(nodeList.item(i).getAttributes().getNamedItem("id").getTextContent());
				xmlElement.setTextContent(nodeList.item(i).getFirstChild().getFirstChild().getFirstChild().getFirstChild().getNodeValue());
				xmlDoc.getFirstChild().appendChild(xmlElement);
		        System.out.println(nodeList.item(i).getFirstChild().getFirstChild().getFirstChild().getFirstChild().getNodeValue()); 
		    }
			/*XPathFactory factory = XPathFactory.newInstance();
			XPath xpath = factory.newXPath();
			XPathExpression expr = xpath.compile("//div[id='question']/table/tr/td/text()");
			Object result = expr.evaluate(htmlElement, XPathConstants.NODESET);
		    NodeList nodes = (NodeList) result;
		    for (int i = 0; i < nodes.getLength(); i++) {
		        System.out.println(nodes.item(i).getNodeValue()); 
		        xmlDoc.appendChild(nodes.item(i));
		    }*/

			
			//testDoc.getFirstChild().appendChild(testElement);
			saveFile(xmlDoc, xmlFilePath);
		}catch (Exception e) {
			e.printStackTrace();
		}

	}
	public static void updateXML() throws Exception{
		Document doc = getDOMObject("F:\\test.xml");
		dumpNode(doc, "");
		Element newElement = doc.createElement("testElement");
		newElement.setAttribute("attr", "value");
		newElement.setTextContent("test ");
		doc.getFirstChild().appendChild(newElement);
		dumpNode(doc, "");
		saveFile(doc, "F:\\test.xml");
	}
	
	/**
	 * Dumps / prints a document or a node
	 * @param Document / Node
	 * @param indentation
	 */
	public static void dumpNode(Node node, String indentation)
    {
        if (node == null) {
            return;
        }

        int type = node.getNodeType();

        switch (type) {
            case Node.DOCUMENT_NODE: {
                displayText[numberLines] = indentation;
                displayText[numberLines] +=
                    "<?xml version=\"1.0\" encoding=\""+
                  "UTF-8" + "\"?>";
                System.out.println(displayText[numberLines]);
                numberLines++;
                dumpNode(((Document)node).getDocumentElement(), "");
                break;
             }

             case Node.ELEMENT_NODE: {
                 displayText[numberLines] = indentation;
                 displayText[numberLines] += "<";
                 displayText[numberLines] += node.getNodeName();

                 int length = (node.getAttributes() != null) ?
                     node.getAttributes().getLength() : 0;
                 Attr attributes[] = new Attr[length];
                 for (int loopIndex = 0; loopIndex < length; loopIndex++) {
                     attributes[loopIndex] =
                         (Attr)node.getAttributes().item(loopIndex);
                 }

                 for (int loopIndex = 0; loopIndex < attributes.length;
                     loopIndex++) {
                     Attr attribute = attributes[loopIndex];
                     displayText[numberLines] += " ";
                     displayText[numberLines] += attribute.getNodeName();
                     displayText[numberLines] += "=\"";
                     displayText[numberLines] += attribute.getNodeValue();
                     displayText[numberLines] += "\"";
                 }
                 displayText[numberLines] += ">";
                 System.out.println(displayText[numberLines]);
                 
                 numberLines++;

                 NodeList childNodes = node.getChildNodes();
                 if (childNodes != null) {
                     length = childNodes.getLength();
                     indentation += "    ";
                     for (int loopIndex = 0; loopIndex < length; loopIndex++ ) {
                        dumpNode(childNodes.item(loopIndex), indentation);
                     }
                 }
                 break;
             }

             case Node.TEXT_NODE: {
                 displayText[numberLines] = indentation;
                 String trimmedText = node.getNodeValue().trim();
                 if(trimmedText.indexOf("\n") < 0 && trimmedText.length() > 0){
                     displayText[numberLines] += trimmedText;
                     System.out.println(displayText[numberLines]);
                     numberLines++;
                 }
                 break;
             }

             case Node.PROCESSING_INSTRUCTION_NODE: {
                 displayText[numberLines] = indentation;
                 displayText[numberLines] += "<?";
                 displayText[numberLines] += node.getNodeName();
                 String text = node.getNodeValue();
                 if (text != null && text.length() > 0) {
                     displayText[numberLines] += text;
                 }
                 displayText[numberLines] += "?>";
                 System.out.println(displayText[numberLines]);
                 numberLines++;
                 break;
             }

             case Node.CDATA_SECTION_NODE: {
                 displayText[numberLines] = indentation;
                 displayText[numberLines] += "<![CDATA[";
                 displayText[numberLines] += node.getNodeValue();
                 displayText[numberLines] += "]]>";
                 System.out.println(displayText[numberLines]);
                 numberLines++;
                 break;
            }
        }

        if (type == Node.ELEMENT_NODE) {
            displayText[numberLines] = indentation.substring(0,
                indentation.length() - 4);
            displayText[numberLines] += "</";
            displayText[numberLines] += node.getNodeName();
            displayText[numberLines] += ">";
            System.out.println(displayText[numberLines]);
            numberLines++;
            indentation += "    ";
        }
    }
	/**
	 * This method validates an xml file and returns a DOM reprsentation in the
	 * form of a Document instance. This DOM provides primary access to the
	 * file's data
	 * 
	 * @param fileNamePath
	 *            name of the xml file
	 * @return dom object of the newFeed file
	 * @throws Exception
	 */
	public static Document getDOMObject(String fileNamePath) throws Exception {
		Document localDom = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			localDom = builder.parse(new File(fileNamePath));
			// java.io.InputStream is = new java.io.InputStream();
			//localDom = builder.parse(fileNamePath);
			if (localDom == null) {
				localDom = builder.newDocument();
			}
			localDom.getDocumentElement().normalize();
		} catch (SAXException sxe) {
			System.out
					.println("Error generated during parsing the file " + sxe);
			Exception x = sxe;
			if (sxe.getException() != null)
				x = sxe.getException();
			throw x;
		} catch (ParserConfigurationException pce) {
			System.out.println("Parser with specified options can't be built "
					+ pce);
			throw pce;
		} catch (IOException ioe) {
			System.out.println("IO Error " + ioe);
			throw ioe;
		}
		localDom.normalize();
		return localDom;
	}

	/**
	 * Returns Document object from an input stream
	 * 
	 * @param input
	 * @return
	 * @throws Exception
	 */
	public static Document getDomObjectFromInputStream(InputStream input)
			throws Exception {
		Document localDom = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			// localDom = builder.parse(new File(fileNamePath));
			// java.io.InputStream is = new java.io.InputStream();
			localDom = builder.parse(input);
			if (localDom == null)
				localDom = builder.newDocument();
			/**
			 * Normalize to remove extra spaces
			 */
			localDom.getDocumentElement().normalize();

		} catch (SAXException sxe) {
			System.out
					.println("Error generated during parsing the file " + sxe);
			Exception x = sxe;
			if (sxe.getException() != null)
				x = sxe.getException();
			throw x;
		} catch (ParserConfigurationException pce) {
			System.out.println("Parser with specified options can't be built "
					+ pce);
			throw pce;
		} catch (IOException ioe) {
			System.out.println("IO Error " + ioe);
			throw ioe;
		}
		localDom.normalize();
		return localDom;
	}

	
	/**
	 * Creates a blank xml document in memory
	 * 
	 * @return Document
	 */
	public static Document getNewDOMObject() {
		Document localDom = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setValidating(true);
			factory.setNamespaceAware(true);
			factory.setIgnoringElementContentWhitespace(true);
			DocumentBuilder builder = factory.newDocumentBuilder();
			localDom = builder.newDocument();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return localDom;
	}

	/**
	 * This method adds a line break in output xml
	 * 
	 * @param newDrugDoc
	 *            Document in which line break to be added
	 * @param divElement
	 *            Element after which line break to be added
	 */
	public static void indent(Document newDrugDoc, Element divElement, String str) {
		Text textElement = newDrugDoc.createTextNode(str);
		divElement.appendChild(textElement);
	}

	/**
	 * Writes a document in memory to a file on disk
	 * 
	 * @param newDrugDoc
	 * @param fileName
	 * @throws Exception
	 */
	private static void saveFile(Document newDrugDoc, String filePath) throws Exception {
		Transformer transformer = TransformerFactory.newInstance().newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		StreamResult result = new StreamResult(new StringWriter());
		DOMSource source = new DOMSource(newDrugDoc);
		transformer.transform(source, result);
		String xmlString = result.getWriter().toString();
		BufferedWriter out = new BufferedWriter(new FileWriter(filePath));
		out.write(xmlString);
		out.close();
	}

	/**
	 * Transforms xml to html using xsl
	 * 
	 * @param xmlPath
	 * @param xslPath
	 * @param outputPath
	 */
	public static void transform(String xmlPath, String xslPath, String outputPath) {
		try {
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer(new javax.xml.transform.stream.StreamSource(xslPath));
			transformer.transform(new javax.xml.transform.stream.StreamSource(
					xmlPath), new javax.xml.transform.stream.StreamResult(
					new FileOutputStream(outputPath)));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static NodeList getXPath(Document doc, String xPath) throws Exception {
		XPathFactory factory = XPathFactory.newInstance();
		XPath xpath = factory.newXPath();
		XPathExpression expr = xpath.compile(xPath);
		Object result = expr.evaluate(doc, XPathConstants.NODESET);
		NodeList nodes = (NodeList) result;
		return nodes;
	}
}
